#pragma once
#include"Util.hpp"
#include"Log.hpp"
#include<iostream>
#include<algorithm>
#include<sstream>
#include<unordered_map>
#include<string>
#include<vector>
#include<unistd.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<sys/sendfile.h>

const std::string SEG = ": ";
const std::string IFQUERY = "?";

#define LINEEND "\r\n"
#define PAGE "index.html"
#define NOT_FOUNT_PAGE "./Web/404Error.html"
#define Internal_Server_Error_PAGE "./Web/500Error.html"
#define WEBROOT "./Web"
#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define Internal_Server_Error 500

static std::string Suffix2Desc(const std::string &suffix) {
    static std::unordered_map<std::string, std::string> TypeMap = {
       {".html", "text/html"},
       {".css", "text/css"},
       {".jpg", "image/jpeg"},
       {".png", "image/png"},
       {".txt", "text/plain"},         
       {".js", "application/javascript"},
       {".xml", "application/xml"}
    };

    auto iter = TypeMap.find(suffix);
    if (iter != TypeMap.end()) {
        return iter->second;
    }
    return "text/html";
}

class HttpRequest {
    public:
       std::string _request_lines;
       std::vector<std::string> _request_header;
       std::string _blank;
       std::string _request_body;

       //将已经分离读取到的信息划分出来并保存离
       std::string _method;
       std::string _url;
       std::string _version;

       std::unordered_map<std::string, std::string> _kvMap;/*用来请求正文中的key和value*/

       std::string _path;/*保存资源路径,方便打开对应的资源*/
       std::string _suffix;/*用于保存GET方法切割url后?后的参数*/
       bool _cgi;/*记录是否需要cgi机制*/
       int _Content_Length;/*用来保存读取的正文长度*/
    public:
       HttpRequest() : _cgi(false), _Content_Length(0) {

       }
       ~HttpRequest() {

       }
};

class HttpResponse {
    public:
       std::string _status_line;  
       std::vector<std::string> _response_header;
       std::string _blank;/*默认给空行*/
       std::string _response_body;
       /*用来记录一些要发送的信息,方便维护*/
       int _status_code;/*标记返回的状态码，方便构造状态信息和解耦*/
       int _size;/*记录返回文件的大小*/
       int _fd;/*用来保存打开的文件描述符,sendfile减少拷贝*/
    public:
       HttpResponse() :_blank(LINEEND), _status_code(OK) {};
       ~HttpResponse() {}
};

class EndPoint {
    private:
      int _sock;
      HttpRequest _http_request; 
      HttpResponse _http_response;
    private:
      bool RecvHttpRequestLine() {
           std::string& out = _http_request._request_lines;
           if (Util::ReadLine(_sock, out) <= 0) {
              return false;
           }
           return true;
      }
      void ParseHttpRequestLine() {
           std::stringstream ss(_http_request._request_lines);/*默认按空行分割*/
           ss >> _http_request._method >> _http_request._url >> _http_request._version;
           Util::Tranform(_http_request._method);
           LOG(INFO, _http_request._method);
           LOG(INFO, _http_request._url);
           LOG(INFO, _http_request._version);
      }
      int RecvHttpRequestHeader() {
           std::string out;
           while (true) {
               out.clear();
               size_t s = Util::ReadLine(_sock, out);  
               if (s == 1) { /*只有'\n',说明读到了空行读完了*/
                   break; 
               }
               if (s <= 0) {
                   return false;
               }
               out.resize(static_cast<int>(out.size()) - 1);
               _http_request._request_header.push_back(out);
               LOG(INFO ,out);
           }              
           return true;
      }
      void ParseHttpRequestHeader() {
        for(auto& str: _http_request._request_header) {
             std::string key, value;
             Util::CutStrToPart(str, key, value, SEG);
             _http_request._kvMap.insert({key,value});
             LOG(INFO, key);
             LOG(INFO, value);
        }
      }
      bool IsHasHttpRequestBody() {
           auto& str = _http_request._method;
           if (str == "POST") {
               return true; 
           }
           return false;
      }
      void RecvHttpRequestBody() {
        if (IsHasHttpRequestBody()) {
            auto iter = _http_request._kvMap.find("Content-Length");
            if (iter == _http_request._kvMap.end()) {
                LOG(ERROR, "POST NOT FOUND Content-Length");
            }
            else {
                _http_request._Content_Length = atoi((iter -> second.c_str()));
                LOG(INFO, "Content-Length get success");
                int contentLength = _http_request._Content_Length;

                char ch = 0;
                while (contentLength) {
                     size_t s = recv(_sock, &ch, 1, 0);
                     if (s == 0) {
                         LOG(ERROR, "contentLength"); 
                         break;
                     }
                     _http_request._request_body.push_back(ch);
                     contentLength--;
                }
            }
        }  
      }
      int ProcessNonCgi() {
          _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
          if (_http_response._fd < 0) {
              LOG(ERROR, "ProcessNonCgi open error");
              return Internal_Server_Error;
          }
          return OK;
      }
      int ProcessCgi() { /*GET带参或者POST方法才会到这里调用cgi程序*/

          int code = OK;

          std::string& bin = _http_request._path; /*这里的一定是合法的*/
          
          LOG(INFO , bin);

          std::string PATH = "METHOD=";
          PATH += _http_request._method;
          putenv((char*)PATH.c_str());/*注意Putenv是浅拷贝*/

          std::string DATA = "INPUT=";

          if (_http_request._method == "GET") {
              DATA += _http_request._suffix;
              LOG(INFO, DATA);
              putenv((char*)DATA.c_str());
          }

          std::string LENGTH =  "CONTENT_LENGTH=";
          if (_http_request._method == "POST") {
              LENGTH += std::to_string(_http_request._request_body.size());
              putenv((char*)LENGTH.c_str());
          }
          else {
              LENGTH += std::to_string(_http_request._suffix.size());
          }

          LOG(INFO, LENGTH);

          int input[2], output[2]; 
          if (pipe(input) < 0) {
             LOG(FATAL, "INPUT PIPE CREATE FAILED");
             code = Internal_Server_Error;
          } 
          if (pipe(output) < 0) {
             LOG(FATAL, "OUTPUT PIEPE CREATE FAILED");
             code = Internal_Server_Error;
          } 
          pid_t pid = fork();
          if (pid == 0) {/*子进程*/
              close(input[0]);
              close(output[1]);

              LOG(INFO , bin);
             
              dup2(input[1], 1);
              dup2(output[0], 0);

              execl(bin.c_str(), bin.c_str(), nullptr);
              LOG(ERROR, "execl error");
              exit(1);
          }
          else if (pid > 0) { /*父进程*/
              close(input[1]);        
              close(output[0]);

              /*通过管道传输正文*/
              if (_http_request._method == "POST") {
                  int cnt = 0;
                  char* str = (char*)_http_request._request_body.c_str();
                  size_t size = _http_request._request_body.size();
                  size_t s;
                  while ((s = write(output[1], str + cnt, size - cnt)) > 0 ) {
                        cnt += s; 
                  }
              } 
              int status = 0;
              pid_t res = waitpid(pid, &status, 0);

              if (res == pid) {
                   if (WIFEXITED(status)) {/*正常退出*/
                     if (WEXITSTATUS(status)) {/*退出码 > 0*/
                        LOG(ERROR, "cgi exitcode is > 0");
                        code = BAD_REQUEST;  
                     }    
                     else {
                        code = OK;
                     }
                   }
                   else if (WIFSIGNALED(status)) {/*异常退出*/
                       LOG(ERROR, "cig is killed");
                       code = Internal_Server_Error;
                   }
              }

              /*读子进程返回的数据*/
              size_t size; 
              char ch = '0';
              while ((size = read(input[0], &ch, 1)) > 0) {
                   _http_response._response_body.push_back(ch); 
              }
              LOG(INFO, _http_response._response_body);
              LOG(INFO, std::to_string(_http_response._response_body.size()));
              /*释放资源*/
              close(input[0]);
              close(output[1]);
          }
          else {
              LOG(FATAL, "fork error.."); 
              return  Internal_Server_Error;
          }
          LOG(INFO, "ProcessCgi() done...");
          return code;
      }
      void HandleResponseStatusLine() {
             /*构建响应状态行*/
             _http_response._status_line += "HTTP/1.0";
             _http_response._status_line += " ";
             _http_response._status_line += std::to_string(_http_response._status_code);
             _http_response._status_line += " ";
             _http_response._status_line += Util::Code2Desc(_http_response._status_code);
             _http_response._status_line += LINEEND;
      }
      void HandleOK() {

             /*响应报头中插入Content-Length属性*/
             std::string ContentLength = "Content-Length: ";
             if (_http_request._cgi == false) { /*返回静态网页*/
                 ContentLength += std::to_string(_http_response._size);
             }
             else {
                 ContentLength += std::to_string(_http_response._response_body.size());
             }
             ContentLength += LINEEND;
             _http_response._response_header.push_back(ContentLength);

             /*响应报头中插入Content-Type属性,这里的path一定是合法的*/
             std::string ContentType = "Content-Type: ";

             if (_http_request._cgi == false) {

                  size_t pos = _http_request._path.rfind(".");  
                  if (pos == std::string::npos) {
                      LOG(ERROR, "dot is not found");
                  }
  
                  std::string suffix = _http_request._path.substr(pos);

                  ContentType += Suffix2Desc(suffix);
                  ContentType += LINEEND;

             }
             else {
                  std::string suffix = ".html";
                  ContentType += Suffix2Desc(suffix);
                  ContentType += LINEEND;
             }

             _http_response._response_header.push_back(ContentType);
             /*空行初始化给了*/
      }
      void HandleError(std::string page) {
              
             /*不管什么方法到这里都是差错处理，要进行静态网页的返回,因此此时不调用cgi(输出正文)输出响应*/
             _http_request._cgi = false;
             std::string ContentLength = "Content-Length: ";
             struct stat st;
             int result = stat(page.c_str(), &st);
             if (result < 0) {
                LOG(ERROR, "Error page not exists");
             } 
             ContentLength += std::to_string(st.st_size);
             ContentLength += LINEEND; 
             _http_response._response_header.push_back(ContentLength);


             std::string ContentType = "Content-Type: text/html";
             ContentType += LINEEND;
             _http_response._response_header.push_back(ContentType); 
             
             LOG(INFO, std::to_string(_http_request._cgi));
             _http_response._fd = open(page.c_str(), O_RDONLY);
             _http_response._size = st.st_size;

      }
      void Code2Response() {
               
           int code = _http_response._status_code;
           LOG(INFO ,std::to_string(code));

           HandleResponseStatusLine();
           switch (code) {
             case OK :
               LOG(INFO, "HandleOK");
               HandleOK();
               break; 
             case NOT_FOUND:
               LOG(INFO, "HandleError NOT_FOUND");
               HandleError(NOT_FOUNT_PAGE);
               break;
             case Internal_Server_Error:
               LOG(INFO, "HandleError ServerERROR");
               HandleError(Internal_Server_Error_PAGE);
               break;
             default:
               break;
           }
      }
    public:
      EndPoint(int sock) :_sock(sock) {

      }
      int RecvHttpRequest() {
           if (RecvHttpRequestLine() && RecvHttpRequestHeader()) {
              ParseHttpRequestLine();
              ParseHttpRequestHeader();
              RecvHttpRequestBody();
              return true;
           }
           else {
              return false;
           }
      }
      void BuildHttpResponse() {

        std::string& path = _http_request._path;
        std::string& argv = _http_request._suffix;   

        int& code = _http_response._status_code;

        if (_http_request._method != "POST" && _http_request._method != "GET") {
            code = BAD_REQUEST;
            goto END;
        }
        if (_http_request._method == "GET" ) {
          if (_http_request._url.find(IFQUERY) != std::string::npos) { /*说明是发送*/
              Util::CutStrToPart(_http_request._url, path, argv, IFQUERY);
              path = WEBROOT + path;
              LOG(INFO, path);
              LOG(INFO, argv);
              _http_request._cgi = true;
          }
          else { /*说明是获取*/
            if ( _http_request._url == "/") {
                path = WEBROOT + std::string("/") + std::string(PAGE);
            }
            else  path = WEBROOT + _http_request._url;
            LOG(INFO, path);
            /*判断当前文件是否存在*/
            struct stat st; 
            int cnt = stat(path.c_str(), &st);
            LOG(INFO, path);
            if (cnt < 0) {/*说明资源不存在*/
               code = NOT_FOUND;
               LOG(ERROR, "FILE NOT FOUND");
               goto END;
            } 
            /*说明是目录*/ 
            if (S_ISDIR(st.st_mode)) {
                 path += "/";
                 path += PAGE; 
                 cnt = stat(path.c_str(), &st); 
                 LOG(INFO, path);
                 if (cnt < 0) {
                     code = NOT_FOUND;
                     LOG(ERROR, "Dir not found index.html");
                     goto END;
                 }
                 LOG(INFO, std::string("Dir -> html :") + path);
            }
            /*说明是可执行程序*/
            if ((S_IXUSR&st.st_mode) || (S_IXGRP&st.st_mode) || (S_IXOTH&st.st_mode)) {
                 _http_request._cgi = true;
            }
            
            _http_response._size = st.st_size;
          }
        }
        else if (_http_request._method == "POST") { /*说明是发送*/
            _http_request._cgi = true;
            path = WEBROOT +  _http_request._url;
        }

        if ( _http_request._cgi == false) {
            LOG(INFO, "Call ProcessNonCgi");
            code = ProcessNonCgi();
        }
        else {
            LOG(INFO, "Call ProcessCgi");
            code = ProcessCgi();
        } 
END:
        /*统一通过code进行构建响应报文*/
        /*降低耦合*/
        Code2Response(); 

        return;
      }

      void SendHttpResponse() {
           LOG(INFO, "SendHttpResponse");
           send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);
           for (auto& iter: _http_response._response_header) {
                send(_sock, iter.c_str(), iter.size(), 0);
                LOG(INFO, iter.c_str());
           }
           send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);

           ssize_t s = 0;
           if (_http_request._cgi == false) {
                s = sendfile(_sock, _http_response._fd, nullptr, _http_response._size);
                LOG(INFO, "sendfile: " + std::to_string(_http_response._size));
           }
           else if (_http_request._cgi == true) {
								auto& response_body = _http_response._response_body;
                LOG(INFO, response_body);
								size_t total = 0;
								char* start = (char*)response_body.c_str();
								while (total < response_body.size() && (s = send(_sock, start + total, response_body.size() - total, 0)) > 0) {
										total += s;
                    LOG(INFO, std::to_string(total));
								} 
                LOG(INFO, "send-s : " + std::to_string(total));
           }

           std::cout<<"errno = " <<errno <<std::endl;
           close(_http_response._fd);
      }
      ~EndPoint() {
          close(_sock);
      }
};

//#define DEBUG 1
class CallBack{
    public:
      CallBack() = default; 
      void operator()(int _sock) { /*仿函数调回调函数*/
           HandlerRequest(_sock);
      }
      static void* HandlerRequest(int sock) {
          
#ifdef DEBUG
          char buffer[4096];
          size_t s = recv(sock, buffer, sizeof(buffer), 0);
          std::cout<<"---------begin-----------"<<std::endl;
          std::cout<<buffer<<std::endl;
          std::cout<<"---------end-------------"<<std::endl;
#else
          EndPoint* ep = new EndPoint(sock);
          if (ep -> RecvHttpRequest()) {
              ep -> BuildHttpResponse();
              ep -> SendHttpResponse();
          }
          else {
              LOG(INFO, "RecvHttpRequest ERROR");
          }
          delete ep;
#endif
          
          LOG(INFO, "Hander Request End");
          return nullptr;
      }
};
